#include "signlanguage.h"

/* Constructor
 * Creates a connection to the webcam, and attempts to get a frame
 * from it. Does extensive error checking, giving informative error
 * messages to the user if any of the steps fail. Then reads in the
 * search database file. Finally it sets all the boolean variables
 * and starts the QTimer (which deals with all webcam playing/recording).
 */
SignLanguage::SignLanguage(QWidget *parent)
    : QMainWindow(parent)
{
    //create camera object
    camera = cvCreateCameraCapture(0);
    hasCamera = true;
    //check its working
    if(camera == NULL)
    {
        //Error checking========================================
        QMessageBox msgBox;
        msgBox.setText("Failed to make a connection to video input device!");
        msgBox.setInformativeText("Would you like to continue running the program without webcam access?");
        msgBox.setStandardButtons(QMessageBox::Yes | QMessageBox::No);
        msgBox.setDefaultButton(QMessageBox::Yes);
        msgBox.setIcon(QMessageBox::Critical);
        QString detail = "The program could not get access to a video input device. This could be because "
                         "their is another instance of this program already running, or because some other "
                         "program is busy using the webcam. This could also be caused by not having a "
                         "correctly installed webcam on this system.";
        msgBox.setDetailedText(detail);
        int ret = msgBox.exec();
        if(ret == QMessageBox::Yes)
        {
            hasCamera = false;
        }
        else
        {
            //run assertion, which will terminate the program
            assert(camera);
        }
        
        //End Error checking====================================
    }
    if(hasCamera)
    {
        //get a frame from camera
        IplImage * image=cvQueryFrame(camera);
        //check that it was successfull
        if(image == NULL)
        {
            //Error checking========================================
            QMessageBox msgBox;
            msgBox.setText("Failed to retrieve a video frame from camera!");
            msgBox.setInformativeText("See details for more information.");
            msgBox.setStandardButtons(QMessageBox::Ok);
            msgBox.setDefaultButton(QMessageBox::Ok);
            msgBox.setIcon(QMessageBox::Critical);
            QString detail = "The program could not retrieve any video frames from the systems "
                             "webcam. This could be caused by a faulty or intermittant webcamera. "
                             "Make sure that your webcam is plugged in and properly functional and "
                             "then try to start the program again.";
            msgBox.setDetailedText(detail);
            msgBox.exec();
            //run assertion which will close the program in an error code, specifying where the error occured.
            assert(image);
            //End Error checking====================================
        }
    }

    playingWebcam = false;
    recording = false;
    webcamTabOpen = false; //default tab is picture
    flashRecordingSquare = true;
    playingStoredClip = false;
    paused = false;

    dir = QDir::currentPath(); //get current working directory path and set default video and image

    video_filename =  "file:"+ dir + "/database/deaf.wmv";
    image_filename = ""+dir+"/icons and images/welcome.jpg";

    vidRec = VideoContainer();
    frameCounter = 0;
    playRecording = false;
    frameRateOffseter = 0;

    //finally start off our timer which will run for the duration of the program    
    timer = new QTimer(this);

    //make instance of search engine        
    searchEngine.hide();
    uis.setupUi(&searchEngine);
    uis.retranslateUi(&searchEngine);
    searchEngine.startConnections(&uis);

    //set up shortcut to bring up search window
    search_shortcut = new QShortcut(QKeySequence(tr("Ctrl+s")), this);
}

/* StartConnections Method
 * This method sets up the connection between all the signals and slots,
 * as well as setting up the widgets
 */
void SignLanguage::startConnections(Ui_MainWindow* UI)
{
    ui = UI;

    //make camera widget
    webcam = new WebcamVideoPlayer(this);
    ui->webcamLayout->addWidget(webcam);

    //video player stuff
    clip = new Phonon::MediaObject(this);
    Phonon::createPath(clip, ui->player);

    //Set Scale Mode
     ui->player->setScaleMode(Phonon::VideoWidget::FitInView);

     //set up defaults
     ui->picture->setPixmap(QPixmap(image_filename));
     clip->setCurrentSource(Phonon::MediaSource(video_filename));
     ui->picture->setScaledContents(true);

    //Make Button Connections
    connect(ui->play1Button, SIGNAL(clicked()),this,SLOT(play1()));
    connect(ui->play2Button, SIGNAL(clicked()),this,SLOT(play2()));
    connect(ui->playBothButton, SIGNAL(clicked()),this,SLOT(playBoth()));
    connect(ui->stop1Button, SIGNAL(clicked()),this,SLOT(stop1()));
    connect(ui->stop2Button, SIGNAL(clicked()),this,SLOT(stop2()));
    connect(ui->recordButton, SIGNAL(clicked()),this,SLOT(record()));
    connect(ui->deleteButton, SIGNAL(clicked()),this,SLOT(deleteClicked()));
    connect(ui->searchButton, SIGNAL(clicked()),this,SLOT(search()));
    if(hasCamera)
    {
        connect(ui->tabWidget, SIGNAL(currentChanged(QWidget *)),this,SLOT(tabChanged()));
    }else
    {
        ui->tab_6->setEnabled(false);
        //ui->tabWidget->setEnabled(false);
    }
    connect(clip, SIGNAL(finished()),this,SLOT(videoFinished()));
    connect(clip, SIGNAL(currentSourceChanged(const Phonon::MediaSource &)),this,SLOT(play1()));
    connect(uis.okButton, SIGNAL(clicked()),this,SLOT(okClicked()));
    connect(search_shortcut, SIGNAL(activated()), this, SLOT(search()));
    //Connect our timer
    connect(timer, SIGNAL(timeout()), this, SLOT(updateInterval()));

    //and start the timer
    timer->start(30);
}

/* TabChanged Slot
 * This slot handles the changes required when the tabs are changed.
 * It keeps track of which tab was open before the tab was changed.
 * This allows it to call the appropriate methods and enables the needed buttons.
 */
void SignLanguage::tabChanged()
{
    if(!webcamTabOpen)//webcam tab
    {
        webcamTabOpen = true;
        playingWebcam = false;
        play2();
        ui->play2Button->setEnabled(true);       
        ui->playBothButton->setEnabled(false);
        ui->deleteButton->setEnabled(false);
        ui->recordButton->setEnabled(true);
        ui->stop2Button->setEnabled(false);
    }
    else//image tab --> the default
    {
        webcamTabOpen = false;
        stop2();
        deleteClicked();
        ui->play2Button->setEnabled(false);  
        ui->playBothButton->setEnabled(false);
        ui->deleteButton->setEnabled(false);
        ui->recordButton->setEnabled(false);
        ui->stop2Button->setEnabled(false);
    }
}

/* Play1 Slot
 * This slot is called whenever the play button on the left hand side of the screen is clicked
 * It first checks if there is something that could be played and give an appropriate error message if not.
 * If the play1Button is currently a 'play' icon, the video will start playing and the icon will change to a pause icon.
 * The tooltip will also change according. If the button is set to a 'pause' icon, this slot will pause the video,
 * change the tooltip and return the icon 'play'.
 */
void SignLanguage::play1()
{
    if(!playingStoredClip)
    {
        if(clip->totalTime() > 10)
        {
            clip->play();
            QIcon icon;
            icon.addFile(QString::fromUtf8(":/images/icons and images/Button-Pause.png"), QSize(), QIcon::Normal, QIcon::Off);
            ui->play1Button->setIcon(icon);
            ui->play1Button->setToolTip("Pause");
            playingStoredClip = true;
        }
        else//Must have a falacious database entry
        {
            //Error checking========================================
            QMessageBox msgBox;
            msgBox.setText("Failed to play video file!\t\t");
            msgBox.setInformativeText("Would you like to search for a different word instead?");
            msgBox.setStandardButtons(QMessageBox::Yes | QMessageBox::No | QMessageBox::Cancel);
            msgBox.setDefaultButton(QMessageBox::Ok);
            msgBox.setIcon(QMessageBox::Warning);
            QString detail = "The program attempted to open a video file specified in the database file"
                             " but failed to open it. This could be because the video file is incorrectly "
                             "named or has been moved or deleted. This could also be because the database "
                             "file contains an erroneous record. You can edit \"searchList.txt\" to fix this "
                             "if you are an experianced computer user.";
            msgBox.setDetailedText(detail);
            int ret  = msgBox.exec();

            switch(ret)
            {
                case QMessageBox::No:
                    //do nothing....
                    break;
                case QMessageBox::Yes:
                    //open search dialog
                    search();
                    break;
                case QMessageBox::Cancel:
                    //do nothing - makes user feel happy to have a cancel key
                    break;
            }
            //End Error checking====================================
        }
    }
    else
    {
        if(paused)
        {
            clip->play();
            QIcon icon;
            icon.addFile(QString::fromUtf8(":/images/icons and images/Button-Pause.png"), QSize(), QIcon::Normal, QIcon::Off);
            ui->play1Button->setIcon(icon);
            ui->play1Button->setToolTip("Pause");
            paused = false;
        }
        else
        {            
            clip->pause();
            QIcon icon;
            icon.addFile(QString::fromUtf8(":/images/icons and images/Button-Play-256x256.png"), QSize(), QIcon::Normal, QIcon::Off);
            ui->play1Button->setIcon(icon);
            ui->play1Button->setToolTip("Play");
            paused = true;
        }
    }
}

/* Play2 Slot
 * This slot will first check if there is any recorded video stored.
 * If so it will play or pause the recorded clip accordingly, changing the icons and tooltips as required.
 * If there was nothing recorded it plays and pauses live webcam feed.
 */
void SignLanguage::play2()
{
    if(!vidRec.isEmpty())
    {        
        if(!playRecording)//play recording
        {
            playRecording = true;
            QIcon icon;
            icon.addFile(QString::fromUtf8(":/images/icons and images/Button-Pause.png"), QSize(), QIcon::Normal, QIcon::Off);
            ui->play2Button->setIcon(icon);
            ui->play2Button->setToolTip("Pause playback");
            timer->setInterval(55);
        }
        else//pause recording
        {
            QIcon icon;
            icon.addFile(QString::fromUtf8(":/images/icons and images/Button-Play-256x256.png"), QSize(), QIcon::Normal, QIcon::Off);
            ui->play2Button->setIcon(icon);
            ui->play2Button->setToolTip("Play back recording");
            playRecording = false;
        }
    }
    else if(!playingWebcam)//playing webcam
    {
        playingWebcam = true;
        QIcon icon;
        icon.addFile(QString::fromUtf8(":/images/icons and images/Button-Pause-Green.png"), QSize(), QIcon::Normal, QIcon::Off);
        ui->play2Button->setIcon(icon);
        ui->play2Button->setToolTip("Pause webcam");
        ui->recordButton->setEnabled(true);
    }
    else//not playing webcam
    {
        playingWebcam = false;
        QIcon icon;
        icon.addFile(QString::fromUtf8(":/images/icons and images/Button-Play-Green256x256.png"), QSize(), QIcon::Normal, QIcon::Off);
        ui->play2Button->setIcon(icon);
        ui->play2Button->setToolTip("Play webcam");
        ui->recordButton->setEnabled(false);
    }
}

/* PlayBoth Slot
 * This slot is used to play back the two video clips simultaniously.
 * If it first calls each stop slots to make sure that the videos start from the beginning again
 * and then it calls the two play slots.
 */
void SignLanguage::playBoth()
{
   stop1();
   stop2();
   play1();
   play2();
}

/* Stop1 Slot
 * This slot is used to stop the database video clip from playing.
 * The play button's icon and tool tip change to 'pause'.
 */
void SignLanguage::stop1()
{
    playingStoredClip = false;
    clip->stop();
    QIcon icon;
    icon.addFile(QString::fromUtf8(":/images/icons and images/Button-Play-256x256.png"), QSize(), QIcon::Normal, QIcon::Off);
    ui->play1Button->setIcon(icon);
    ui->play1Button->setToolTip("Play");
}

/* Stop2 Slot
 * Either stops the recording if it is taking place and changes and enables buttons as needed,
 * Of it stops the playback of a pre-recorded click and changes the buttons and tooltips as needed.
 */
void SignLanguage::stop2()
{    
    if(recording)
    {
        recording = false;
        QIcon icon;
        icon.addFile(QString::fromUtf8(":/images/icons and images/Button-Play-256x256.png"), QSize(), QIcon::Normal, QIcon::Off);
        ui->play2Button->setIcon(icon);
        ui->play2Button->setToolTip("Play recording");

        QIcon icon2;
        icon2.addFile(QString::fromUtf8(":/images/icons and images/Button-Stop-256x256.png"), QSize(), QIcon::Normal, QIcon::Off);
        ui->stop2Button->setIcon(icon2);
        ui->stop2Button->setToolTip("Stop playback");

        ui->play2Button->setEnabled(true);
        ui->playBothButton->setEnabled(true);
        ui->deleteButton->setEnabled(true);
        ui->recordButton->setEnabled(false);
        playingWebcam = false;        
    }
    else
    {
        playRecording = false;
        vidRec.resetRead();
        QIcon icon;
        icon.addFile(QString::fromUtf8(":/images/icons and images/Button-Play-256x256.png"), QSize(), QIcon::Normal, QIcon::Off);
        ui->play2Button->setIcon(icon);
        ui->play2Button->setToolTip("Play recording");
    }
}

/* DeleteClick slot
 * This slot will clear any pre-recorded video from the webcam
 * and return to playing live webcam feed, with the appropriate buttons enabled.
 */
void SignLanguage::deleteClicked()
{
    vidRec.clear();//delete any records that we have
    playRecording = false;
    play2();//return to live webcam view

    QIcon icon;
    icon.addFile(QString::fromUtf8(":/images/icons and images/Button-Stop-Green256x256.png"), QSize(), QIcon::Normal, QIcon::Off);
    ui->stop2Button->setIcon(icon);
    ui->stop2Button->setToolTip("Stop recording");
    ui->deleteButton->setEnabled(false);
    ui->stop2Button->setEnabled(false);
    ui->playBothButton->setEnabled(false);

    //speed up timer for webcam feed
    timer->setInterval(30);
}

/* okClicked slot
 * This slot is called when the ok button in the search window is pressed, indicating that the user want to open a database item
 * This slot then updates the main GUI to reflect the search word in the lineEdit and loads the appropriate video and image.
 * It also hides the search window and enables the main gui.
 */
void SignLanguage::okClicked()
{

    if(uis.listWidget->selectedItems().size()==1)//if there is something selected
    {
        std::vector<searchItem> selected = searchEngine.find(uis.listWidget->selectedItems().front()->text(), true);//only want the name match

        ui->lineEdit->setText(selected.front().getName());//change main gui text in lineEdit to the chosen word
        //update the video and image file names
        video_filename = "file:"+ dir + "/database/"+selected.front().getVideoFileName();
        image_filename = dir + "/database/"+selected.front().getImageFileName();

        playingStoredClip = false;
        ui->tabWidget->setCurrentIndex(0);
        ui->picture->setPixmap(QPixmap(image_filename));
        ui->picture->setScaledContents(true);
        clip->setCurrentSource(Phonon::MediaSource(video_filename));
        ui->play1Button->setEnabled(true);
        ui->stop1Button->setEnabled(true);
    }
    //else ignore search and leave everything as it was --> basically works as a cancel option
    searchEngine.hide();//close the search window
}

/* Search slot
 * This slot is called when the user clicks on the search icon.
 * It will stop any video playback and open the search window
 * where the search results will be displayed.
 */
void SignLanguage::search()
{
    //display new window
    stop1();
    stop2();

    //get inputted search word/phrase from main window and set to search window's text line
    if(ui->lineEdit->text().compare(QString("Type your word here:"))==0)
    {
        searchEngine.show();
        searchEngine.searchAndShow("Type your word here:");
    }
    else
    {
        searchEngine.show();
        searchEngine.searchAndShow(ui->lineEdit->text());
    }
}

/* VideoFinished slot
 * This slot is called when the video has finished being played.
 * It changes the play1 button from 'pause' to 'play'.
 */
void SignLanguage::videoFinished()
{
    QIcon icon;
    icon.addFile(QString::fromUtf8(":/images/icons and images/Button-Play-256x256.png"), QSize(), QIcon::Normal, QIcon::Off);
    ui->play1Button->setIcon(icon);
    ui->play1Button->setToolTip("Play");
    paused = true;
}

/* Record Slot
 * This slot will record the live video feed from the webcam and
 * save it as a video clip to be played back later. It will record
 * for a maximum of 20 seconds.
 */
void SignLanguage::record()
{
    ui->stop2Button->setEnabled(true);
    ui->play2Button->setEnabled(false);
    ui->recordButton->setEnabled(false);
    recording = true;
}

/* UpdateInterval Method
 * This is what code is executed every time the timer ticks. The
 * timer generally ticks 30 times a second, defined by the creation of
 * the timer in the play2() method. The code here gets the height of
 * the tabwindow, then gets a frame from the web cam and then draws
 * that frame to the tab scaled to the correct size (maintaining aspect
 * ratio). The timer slows down when playing back a recording and then speeds
 * back up in webcam mode.
 */
void SignLanguage::updateInterval()
{
    //rendering and resizing of webcam window
    hSize = ui->tab_6->geometry().height();    
    hSize = hSize - 36; //because window is always 36 units bigger than video

    if(playRecording)
    {        
        if(vidRec.hasNext())
        {
            if((frameRateOffseter % 5) != 0)
            {
                IplImage *image = vidRec.readNext();
                webcam->putImage(image, hSize);
            }
            frameRateOffseter++;
        }
        else
        {
            playRecording = false;
            vidRec.resetRead();            
            frameRateOffseter = 0;
            QIcon icon;
            icon.addFile(QString::fromUtf8(":/images/icons and images/Button-Play-256x256.png"), QSize(), QIcon::Normal, QIcon::Off);
            ui->play2Button->setIcon(icon);
            ui->play2Button->setToolTip("Play recording");
            stop2();
        }
    }
    else if(playingWebcam)
    {
        IplImage *image=cvQueryFrame(camera);

        //Error checking========================================
        if(image == NULL)
        {
            QMessageBox msgBox;
            msgBox.setText("Failed to retrieve a video frame from camera!");
            msgBox.setInformativeText("See details for more information.");
            msgBox.setStandardButtons(QMessageBox::Ok);
            msgBox.setDefaultButton(QMessageBox::Ok);
            msgBox.setIcon(QMessageBox::Critical);
            QString detail = "The program could not retrieve any video frames from the systems "
                             "webcam. This could be caused by a faulty or intermittant webcamera. "
                             "Make sure that your webcam is plugged in and properly functional and "
                             "then try to restart the program again.";
            msgBox.setDetailedText(detail);
            msgBox.exec();
            //run assertion which will close the program in an error code, specifying where the error occured.
            assert(image);
        }
        //End Error checking====================================
        if(recording)
        {
            frameCounter++;
            //20 secs of recording time @ 25fps
            if(frameCounter != 500)
            {
                vidRec.addFrame(image);
                //every 20 frames turn the red square on and off
                if(frameCounter%10 == 0)
                {
                    if(flashRecordingSquare)
                    {
                        flashRecordingSquare = false;
                    }
                    else
                    {
                        flashRecordingSquare = true;
                    }
                }
                if(flashRecordingSquare)
                {
                    cvRectangle(image,cvPoint(600,20),cvPoint(570,50),cvScalar(0,0,255),CV_FILLED);
                }
            }
            else
            {
                frameCounter = 0;
                stop2();
            }
        }
        //display frame on our widget
        webcam->putImage(image, hSize);
    }
}

/* Destructor
 * This calls the required constructors and closes the searchEngine.
 */
SignLanguage::~SignLanguage()
{
    //end camera connection
    cvReleaseCapture(&camera);
    vidRec.~VideoContainer();
    searchEngine.close();//close the search window
}
